package com.landtool.lanbase.elasticsearch;

import cn.hutool.core.util.ObjectUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.landtool.lanbase.constant.CacheConsts;
import com.landtool.lanbase.entity.ShipEntity;
import com.landtool.lanbase.entity.TargetChanges;
import com.landtool.lanbase.entity.TgRadar;
import com.landtool.lanbase.utils.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * elasticsearch服务
 */
@Service
public class ElasticServer {
    private static Logger log = LoggerFactory.getLogger(ElasticServer.class);

    @Autowired
    ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private RedisTemplate <String, Object> redisTemplate;

    /**
     * ais预警字段
     */
    private final String TARGETCHANGES = "targetchanges";

    /**
     * 类型
     */
    private static final String TYPE = "_doc";


    /**
     * es批量提交
     *
     * @param list      要提交的list集合
     * @param indexName 要提交的索引名称
     * @param T         提交索引对应的实体类的类型
     * @param num       一次提交多少笔数据
     */
    public void bulkIndex(List <?> list, String indexName, Class <?> T, Integer num) {
        Integer counter = 0;
        try {
            if (!elasticsearchTemplate.indexExists(indexName)) {
                elasticsearchTemplate.createIndex(indexName);
                elasticsearchTemplate.putMapping(T);
            }
            if (list.size() > 0) {
                //解析船只数据
                Gson gson = new GsonBuilder().serializeNulls().create();
                List <IndexQuery> queries = new ArrayList <>();
                for (int i = 0; i < list.size(); i++) {
                    Object object = list.get(i);

                    IndexQuery indexQuery = new IndexQuery();
                    indexQuery.setSource("");
                    //计算点 相差的距离
                    indexQuery.setSource(gson.toJson(object));
                    indexQuery.setIndexName(indexName);
                    indexQuery.setType(TYPE);
                    queries.add(indexQuery);

                    if (counter % num == 0) {
                        log.info(indexName + "提交数据条数 : " + counter);
                        elasticsearchTemplate.bulkIndex(queries);
                        queries.clear();
                    }
                    counter++;
                }
                //不足批的索引最后不要忘记提交
                if (queries.size() > 0) {
                    elasticsearchTemplate.bulkIndex(queries);
                    log.info("推送最后一次条数：" + queries.size());
                }
                elasticsearchTemplate.refresh(indexName);
                log.info(indexName + "数据提交完成.");
            }
        } catch (Exception e) {
            log.info("IndexerService.bulkIndex e;" + e.getMessage());
            e.printStackTrace();
        }
    }


    /**
     * 处理要提交的数据
     */
    public void bulkShipDates() {
        String newKey = CacheConsts.openProvince + "_new";
        if (redisTemplate.hasKey(newKey)) {
            List <ShipEntity> newShipData = (List <ShipEntity>) redisTemplate.opsForValue().get(newKey);
            List <TgRadar> tgRadarList = new ArrayList <>();
            for (int i = 0; i < newShipData.size(); i++) {
                TgRadar tgRadar = new TgRadar(newShipData.get(i));
                tgRadarList.add(tgRadar);
            }
            bulkIndex(tgRadarList, FileUtils.getRadarName(), TgRadar.class, 500);
        } else {
            log.info("bulkShipDates---缓存中占无数据");
        }

        //预测目标
        String resultKey = CacheConsts.openProvince + "_result";
        if (redisTemplate.hasKey(resultKey)) {
            Map <Object, Object> map = redisTemplate.opsForHash().entries(resultKey);
            List <ShipEntity> delectList = (List <ShipEntity>) map.get("Delete");
            for (int i = 0; i < delectList.size(); i++) {
                ShipEntity shipEntity = delectList.get(i);
                List <ShipEntity> xy = getListPoint(shipEntity, 60, 5);

            }
        }
    }

    //航速节 1节(kn)=1海里/时=(1852/3600)m/s  1海里(n mile)=1852m
    public static final Double KN = 1852.0 / 3600;


    public List <ShipEntity> getListPoint(ShipEntity shipEntity, Integer interval, Integer num) {
        List <ShipEntity> list = new ArrayList <>();
        DecimalFormat df = new DecimalFormat(".00");
        for (int i = 0; i < num; i++) {
            shipEntity = getFinshPoint(shipEntity, interval);
            ShipEntity shipEntity1 = ObjectUtil.clone(shipEntity);
            shipEntity1.setLongitude(Double.valueOf(df.format(shipEntity1.getLongitude())));
            shipEntity1.setLatitude(Double.valueOf(df.format(shipEntity1.getLatitude())));
            list.add(shipEntity1);
        }

        return list;
    }


    /**
     * 预测经纬度
     *
     * @param shipEntity
     * @param interval   间隔时间
     * @return
     */
    public ShipEntity getFinshPoint(ShipEntity shipEntity, Integer interval) {

        ArrayList <Double> lonlat = lngLatToXY(shipEntity.getLongitude(), shipEntity.getLatitude());
        ArrayList <Double> arrayList = new ArrayList <>();
        //获取目标点间隔时间后行驶距离
        Double r = (KN * shipEntity.getSpeed()) * (interval + 2);

        //计算预测点经度
        Double forecastLon = lonlat.get(0) + r * Math.sin(shipEntity.getDirection() * Math.PI / 180);
        //计算预测点纬度
        Double forecastLat = lonlat.get(1) + r * Math.cos(shipEntity.getDirection() * Math.PI / 180);

        //将平面作标转为经纬度
        HashMap <String, Double> lngLat = xyToLngLat(forecastLon, forecastLat);
        shipEntity.setLatitude(lngLat.get("lat"));
        shipEntity.setLongitude(lngLat.get("lng"));

        return shipEntity;
    }

    public static final Double f = 6378137.0, p = 0.017453292519943;

    /**
     * 经纬度转平面坐标
     *
     * @param lng 经度
     * @param lat 纬度
     */
    public static ArrayList <Double> lngLatToXY(Double lng, Double lat) {
        ArrayList <Double> arrayList = new ArrayList <>();
        if (lat > 89.999999) {
            lat = 89.999999;
        } else if (lat < -89.999999) {
            lat = -89.999999;
        }
        Double c = lat * p;
        Double x = lng * p * f;
        Double y = f / 2 * Math.log((1 + Math.sin(c)) / (1 - Math.sin(c)));
        arrayList.add(x);
        arrayList.add(y);
        return arrayList;
    }

    /**
     * 平面坐标转经纬度
     *
     * @param x 经度平面坐标
     * @param y 纬度平面坐标
     * @return 经纬度
     */
    public static HashMap <String, Double> xyToLngLat(Double x, Double y) {
        HashMap <String, Double> map = new HashMap <>();
        Double lng = x / 20037508.34 * 180;
        Double lat = y / 20037508.34 * 180;
        lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
        map.put("lat", lat);
        map.put("lng", lng);
        return map;
    }


    /**
     * 处理ais报警的数据
     * ais处理报警，跟保存数据库同步进行
     * 历史数据：
     */
    public void tagetChangesSave() {
        String historyKey = CacheConsts.openProvince + "_historyKey";
        String newKey = CacheConsts.openProvince + "_new";
        if (redisTemplate.hasKey(historyKey)) {
            List <ShipEntity> historyList = (List <ShipEntity>) redisTemplate.opsForValue().get(historyKey);
            List <ShipEntity> newList = (List <ShipEntity>) redisTemplate.opsForValue().get(newKey);
            List <TargetChanges> list = aisProcess(historyList, newList);
            bulkIndex(list, TARGETCHANGES, TargetChanges.class, 500);

        } else {
            //如果没有历史数据的情况
            List <ShipEntity> list = (List <ShipEntity>) redisTemplate.opsForValue().get(newKey);
            redisTemplate.opsForValue().set(historyKey, list);
            redisTemplate.expire(historyKey, 5, TimeUnit.MINUTES);
        }
    }

    /**
     * ais处理过程
     *
     * @param historyList
     * @param newList
     * @return
     */
    private List <TargetChanges> aisProcess(List <ShipEntity> historyList, List <ShipEntity> newList) {
        List <TargetChanges> openOrCloseList = aisOpenOrClose(historyList, newList);
        List <TargetChanges> addList = aisAddOrDelete(historyList, newList, "1");
        List <TargetChanges> deleteList = aisAddOrDelete(newList, historyList, "2");
        openOrCloseList.addAll(addList);
        openOrCloseList.addAll(deleteList);
        return openOrCloseList;
    }

    /**
     * ais打开关闭套用
     *
     * @param historyList
     * @param newList
     */
    private List <TargetChanges> aisOpenOrClose(List <ShipEntity> historyList, List <ShipEntity> newList) {
        List <TargetChanges> list = new ArrayList <>();
        Integer pointer = 0;
        for (int i = 0; i < newList.size(); i++) {
            ShipEntity newTarget = newList.get(i);
            for (int j = pointer; j < historyList.size(); j++) {
                ShipEntity historyTarget = historyList.get(j);
                if (historyTarget.getTarget_id() > newTarget.getTarget_id()) {
                    break;
                }
                pointer++;
                if (historyTarget.getTarget_id().equals(newTarget.getTarget_id())) {
                    Integer lastValue = historyTarget.getMmsi();
                    Integer currValue = newTarget.getMmsi();

                    if (lastValue == null && currValue != null) {
                        //打开
                        TargetChanges targetChanges = new TargetChanges(newTarget);
                        targetChanges.setAlarmset("3");
                        list.add(targetChanges);
                    } else if (lastValue != null && currValue == null) {
                        //关闭
                        TargetChanges targetChanges = new TargetChanges(newTarget);
                        targetChanges.setAlarmset("4");
                        list.add(targetChanges);
                    } else if (lastValue != null && !lastValue.toString().equals(currValue.toString())) {
                        //套用
                        TargetChanges targetChanges = new TargetChanges(newTarget);
                        targetChanges.setAlarmset("5");
                        list.add(targetChanges);
                    }

                }
            }
        }
        return list;
    }


    /**
     * ais 新增，删除
     *
     * @param ShipListA
     * @param ShipListB
     * @param Type      设置类型 1是新增 2是删除
     * @return
     */
    private List <TargetChanges> aisAddOrDelete(List <ShipEntity> ShipListA, List <ShipEntity> ShipListB, String Type) {
        List <TargetChanges> list = new ArrayList <>();
        int pointer = 0;
        for (int i = 0; i < ShipListA.size(); i++) {
            ShipEntity shipEntityA = ShipListA.get(i);
            boolean bool = false;
            for (int j = pointer; j < ShipListB.size(); j++) {
                ShipEntity bShipEntity = ShipListB.get(j);
                if (shipEntityA.getTarget_id().equals(bShipEntity.getTarget_id())) {
                    bool = true;
                    pointer++;
                    break;
                } else if (shipEntityA.getTarget_id() < bShipEntity.getTarget_id()) {
                    break;
                }
            }
            if (!bool) {
                TargetChanges targetChanges = new TargetChanges(shipEntityA);
                targetChanges.setAlarmset(Type);
                list.add(targetChanges);
            }
        }
        return list;
    }


//    /**
//     * 历史数据
//     */
//    private HashMap <String, TargetChanges> eventTable = new HashMap <>();
//    private TargetChanges item = null;
//    private int Time_Second = 120;
//    private int Cache_Size = 15000;
//    private Calendar calendar = Calendar.getInstance();
//    private Iterator <Map.Entry <String, TargetChanges>> entrys = null;
//
//    public TargetChanges process(TargetChanges targetChanges) {
//        TargetChanges ret = null;
//        Object lastValue, currValue;
//        try {
//            String key = String.valueOf(targetChanges.getTarget_id());
//            Date now = new Date();
//            if (eventTable.containsKey(key)) {
//                item = eventTable.get(key);
//                lastValue = item.getMmsi();
//                currValue = targetChanges.getMmsi();
//                if (lastValue == null && currValue != null) {
//                    //打开
//                    ret = outGeoEvent(targetChanges, "3");
//                } else if (lastValue != null && currValue == null) {
//                    //关闭
//                    ret = outGeoEvent(targetChanges, "4");
//                } else if (lastValue != null && !lastValue.toString().equals(currValue.toString())) {
//                    //套用
//                    ret = outGeoEvent(targetChanges, "5");
//                }
//                //处理丢失
//                if ((ret == null && eventTable.size() > Cache_Size)) {
//                    entrys = eventTable.entrySet().iterator();
//                    while (entrys.hasNext()) {
//                        item = entrys.next().getValue();
//                        Date date = new Date(item.getTimestamp());
//                        calendar.setTime(date);
//                        calendar.add(Calendar.SECOND, Time_Second);
//                        if (now.after(calendar.getTime())) {
//                            entrys.remove();
//                            //TODO处理将之前的点连成线
//                            ret = outGeoEvent(item, "2");
//                            break;
//                        }
//                    }
//                }
//                eventTable.remove(key);
//                eventTable.put(key, targetChanges);
//            }
//            //新增目标
//            else {
//                ret = outGeoEvent(targetChanges, "1");
//                eventTable.put(key, ret);
//            }
//        } catch (Exception err) {
//            log.error(err.getMessage());
//            ret = null;
//        }
//        return ret;
//    }
//
//    private TargetChanges outGeoEvent(TargetChanges targetChanges, String s) {
//        TargetChanges ret = null;
//        try {
//            if (targetChanges != null) {
//                targetChanges.setAlarmset(s);
//                ret = targetChanges;
//            }
//        } catch (Exception e) {
//            log.error(e.getMessage());
//            ret = null;
//        }
//        return ret;
//    }

}
